package com.huanyang.aspect;

import com.huanyang.anno.RequestLimiting;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class RequestLimitingAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    // 切点
    @Pointcut("@annotation(requestLimiting)")
    public void controllerAspect(RequestLimiting requestLimiting) {

    }

    // 环绕切面
    @Around("controllerAspect(requestLimiting)")
    public Object doAround(ProceedingJoinPoint joinPoint, RequestLimiting requestLimiting) throws Throwable {
        // 获取注解参数
        long period = requestLimiting.period();
        long limitCount = requestLimiting.count();

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 请求ip
        String ip = request.getRemoteAddr();
        // 请求uri
        String uri = request.getRequestURI();
        // 拼接redis的key值
        String key = "request_limiting_".concat(uri).concat(ip);

        ZSetOperations<String, Long> zSetOperations = redisTemplate.opsForZSet();
        // 获取当前时间
        long currentMs = System.currentTimeMillis();
        // 添加当前时间
        zSetOperations.add(key, currentMs, currentMs);
        // 设置过期时间
        redisTemplate.expire(key, period, TimeUnit.SECONDS);
        // 删除窗口之外的值
        zSetOperations.removeRangeByScore(key, 0, currentMs - period * 1000);
        // 查询访问次数
        Long count = zSetOperations.zCard(key);
        if (count > limitCount) {
            log.error("接口拦截：{},请求超过限制频率【{}次/{}秒】，IP为{}", uri, limitCount, period, ip);
            throw new RuntimeException("接口访问频率太快");
        }
        // 放行
        return joinPoint.proceed();
    }
}
